package com.sunlands.deskmate.message.handler.storager.interceptor.impl;

import com.alibaba.fastjson.JSONObject;
import com.sunlands.deskmate.exception.MessageLimitException;
import com.sunlands.deskmate.exception.SendExceptionUtil;
import com.sunlands.deskmate.message.entity.Messages;
import com.sunlands.deskmate.message.enums.MessageType;
import com.sunlands.deskmate.message.handler.receiver.MessageReceiverFactory;
import com.sunlands.deskmate.message.handler.storager.interceptor.Interceptor;
import com.sunlands.deskmate.utils.CompareStrSimUtil;
import com.sunlands.deskmate.utils.DateUtil;
import com.sunlands.deskmate.utils.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.sunlands.deskmate.config.RedisConfig.PREFIX;

/**
 * 发言监控，直接替换掉对应的敏感词
 */
@Order(10)
@Slf4j
@Component
public class SpeakLimitInterceptor implements Interceptor {
    /**
     * 超过30字的发言内容过期时间，设置0为永不过期 单位：分钟
     */
    private static final long SPEAK_TIME_OUT = 0;
    /**
     * 超过30字的发言内容活跃时长，超过时长没有任何操作，自动删除，单位：分钟
     */
    private static final long SPEAK_MAX_IDLE_TIME = 60;
    private static final String REDIS_CHANNEL_SET = PREFIX + ":user:speak30:set";
    private static final int SPEAK_SIMILARITY = 80;
    private static final int SPEAK_RATIO_COUNT = 4;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SendExceptionUtil sendExceptionUtil;

    @Autowired
    private MessageReceiverFactory messageReceiverFactory;


    @Override
    public boolean interceptor(Messages msgEntity) {

        if (!isLengthLimit(msgEntity.getMessage())) {
            return true;
        }

        RMapCache<String, String> speak30Set = redissonClient.getMapCache(REDIS_CHANNEL_SET + msgEntity.getFromUserId());

        //一小时超过1000次30字以上发言，直接禁言
        if (speak30Set.size() > 1000) {
            //构建错误提示
            Messages errorMessage = buildErrorMessage(msgEntity);
            log.error("用户异常发言，直接禁言，不处理！msgEntity = {}", msgEntity);
            messageReceiverFactory.getReceiver(MessageType.BLACK_LIST_LIMIT.getType())
                    .receive(null, errorMessage);
            sendExceptionUtil.sendException("用户异常发言，直接禁言，不处理！msgEntity = " + JSONObject.toJSONString(msgEntity.toString()), new MessageLimitException());
            return false;
        }

        //超过30次内容，切是重复内容，一个小时只允许发送不超过5次
        if (isLimitSpeak(msgEntity.getMessage(), speak30Set)) {
            //构建错误提示
            Messages errorMessage = buildErrorMessage(msgEntity);
            messageReceiverFactory.getReceiver(MessageType.BLACK_LIST_LIMIT.getType())
                    .receive(null, errorMessage);
            return false;
        }

        speak30Set.putIfAbsent(UUID.randomUUID().toString(),
                msgEntity.getMessage(),
                SPEAK_TIME_OUT,
                TimeUnit.DAYS,
                SPEAK_MAX_IDLE_TIME,
                TimeUnit.MINUTES);
        return true;
    }

    private boolean isLengthLimit(String content) {
        return StringUtils.isNotBlank(content)
                && content.length() > 30;
    }

    private Messages buildErrorMessage(Messages msgEntity) {
        return Messages.builder()
                .requestId(UUID.randomUUID().toString())
                .type(MessageType.SPEAK_LIMIT.getType())
                .ackId(IdGenerator.id())
                .createTime(DateUtil.getStringDate())
                .fromUserId(msgEntity.getFromUserId())
                .toId(msgEntity.getFromUserId())
                .message(MessageType.SPEAK_LIMIT.getName())
                .build();
    }

    private boolean isLimitSpeak(String content, RMapCache<String, String> speak30Set) {
        return speak30Set.values().stream().filter(val -> CompareStrSimUtil.getSimilarityRatio(val, content, true) > SPEAK_SIMILARITY).count() >= SPEAK_RATIO_COUNT;
    }
}